////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_MATH_MATRIX_4_H__
#define __WN_MATH_MATRIX_4_H__

#include "WNMath/inc/Internal/WNMatrixBase.h"

namespace WNMath {
    template <typename Type> class WNVector3;
    template <typename Type> class WNVector4;
    template <typename Type> class WNMatrix4;
    template <typename Type> class WNQuaternion;

    namespace __WNInternal {
        template <typename Type, typename = WNCore::WNEnableWhen<WN_TRUE>::Value>
        class __WNMatrix4Base : public __WNMatrixBase<WNMatrix4<Type>, Type, 4> {
        protected:
            WN_FORCE_INLINE __WNMatrix4Base() {}
        };

        template <typename Type>
        class __WNMatrix4Base<Type, typename WNCore::WNEnableWhen<WNCore::WNIsReal<Type>::Value>::Value> : public __WNMatrixBase<WNMatrix4<Type>, Type, 4> {
        public:
            WN_FORCE_INLINE WNVector3<Type>& TransformVectorInPlace(WNVector3<Type>& _vector) const;
            WN_FORCE_INLINE WNVector3<Type>& TransformVectorInPlace(WNVector3<Type>& _vector, Type& _wComponent) const;
            WN_FORCE_INLINE WNVector4<Type>& TransformVectorInPlace(WNVector4<Type>& _vector) const;
            WN_FORCE_INLINE WNVector3<Type> TransformVector(const WNVector3<Type>& _vector) const;
            WN_FORCE_INLINE WNVector3<Type> TransformVector(const WNVector3<Type>& _vector, Type& _wComponent) const;
            WN_FORCE_INLINE WNVector4<Type> TransformVector(const WNVector4<Type>& _vector) const;

            WN_FORCE_INLINE WN_VOID MakeRotation(const WNQuaternion<Type>& _roation);
            WN_FORCE_INLINE WN_VOID MakeRotationX(const Type& _angle);
            WN_FORCE_INLINE WN_VOID MakeRotationY(const Type& _angle);
            WN_FORCE_INLINE WN_VOID MakeRotationZ(const Type& _angle);
            WN_FORCE_INLINE WN_VOID MakeRotationAxis(const Type& _angle, const WNVector3<Type>& _axis);
            WN_FORCE_INLINE WN_VOID MakeRotationAxis(const Type& _angle, const Type& _x, const Type& _y, const Type& _z);

        public:
            static WN_FORCE_INLINE WNMatrix4<Type> CreateRotation() {}
            static WN_FORCE_INLINE WNMatrix4<Type> CreateRotationX() {}
            static WN_FORCE_INLINE WNMatrix4<Type> CreateRotationY() {}
            static WN_FORCE_INLINE WNMatrix4<Type> CreateRotationZ() {}
            static WN_FORCE_INLINE WNMatrix4<Type> CreateRotationAxis() {}
            static WN_FORCE_INLINE WNMatrix4<Type> CreatePerspective(const Type& _fov, const Type& _aspect, const Type& _near, const Type& _far);
            static WN_FORCE_INLINE WNMatrix4<Type> CreateOrthogonal(const Type& _width, const Type& _height, const Type& _near, const Type& _far);
            static WN_FORCE_INLINE WNMatrix4<Type> CreateView(const WNVector3<Type>& _position, const WNVector3<Type>& _target, const WNVector3<Type>& _up);

        protected:
            WN_FORCE_INLINE __WNMatrix4Base() {}
        };
    }

    template <typename Type>
    class WNMatrix4 : public __WNInternal::__WNMatrix4Base<Type> {
    public:
        WN_FORCE_INLINE WNMatrix4() {}
        WN_FORCE_INLINE WNMatrix4(const WNMatrix4& _matrix);
        explicit WN_FORCE_INLINE WNMatrix4(const Type& _number);
        explicit WN_FORCE_INLINE WNMatrix4(const Type* _numbers);

        WN_FORCE_INLINE WN_VOID MakeTranslation(const Type& _translation);
        WN_FORCE_INLINE WN_VOID MakeTranslation(const WNVector3<Type>& _translation);
        WN_FORCE_INLINE WN_VOID MakeTranslation(const Type& _x, const Type& _y, const Type& _z);
        WN_FORCE_INLINE WN_VOID MakeScale(const Type& _scale);
        WN_FORCE_INLINE WN_VOID MakeScale(const WNVector3<Type>& _scale);
        WN_FORCE_INLINE WN_VOID MakeScale(const Type& _x, const Type& _y, const Type& _z);

    public:
        static WN_FORCE_INLINE WNMatrix4 CreateTranslation(const Type& _translation);
        static WN_FORCE_INLINE WNMatrix4 CreateTranslation(const WNVector3<Type>& _translation);
        static WN_FORCE_INLINE WNMatrix4 CreateTranslation(const Type& _x, const Type& _y, const Type& _z);
        static WN_FORCE_INLINE WNMatrix4 CreateScale(const Type& _scale);
        static WN_FORCE_INLINE WNMatrix4 CreateScale(const WNVector3<Type>& _scale);
        static WN_FORCE_INLINE WNMatrix4 CreateScale(const Type& _x, const Type& _y, const Type& _z);

    private:
        typedef class __WNInternal::__WNMatrixBase<WNMatrix4<Type>, Type, 4> Base;
    };

    template <typename Type>
    WN_FORCE_INLINE WNMatrix4<Type> operator + (const Type& _number, const WNMatrix4<Type>& _matrix);

    template <typename Type>
    WN_FORCE_INLINE WNMatrix4<Type> operator - (const Type& _number, const WNMatrix4<Type>& _matrix);

    template <typename Type>
    WN_FORCE_INLINE WNMatrix4<Type> operator * (const Type& _number, const WNMatrix4<Type>& _matrix);

    template <typename Type>
    WN_FORCE_INLINE WNMatrix4<Type> operator / (const Type& _number, const WNMatrix4<Type>& _matrix);
}

#include "WNMath/inc/Internal/WNMatrix4.inl"

#endif // __WN_MATH_MATRIX_4_H__